package org.vanbart.flippo;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.RadioButton;
import android.widget.Toast;

/**
 * Main game class. Handles setting up the board and playing the game.
 * @author ton
 */
public class FlippoActivity extends Activity implements SensorEventListener {
	
	private static final String TAG = "FlippoActivity";
	
	/**
	 * Contains the rules of the game: which buttons to toggle when each of the 9 buttons is clicked.
	 */
	private static final Map<String, int[]> rules = new HashMap<String, int[]>();
	static {
		rules.put("1", new int[] {1, 3, 4} );
		rules.put("2", new int[] {0, 2} );
		rules.put("3", new int[] {1, 4, 5} );
		rules.put("4", new int[] {0, 6} );
		rules.put("5", new int[] {1, 3, 5, 7} );
		rules.put("6", new int[] {2, 8} );
		rules.put("7", new int[] {3, 4, 7} );
		rules.put("8", new int[] {6, 8} );
		rules.put("9", new int[] {4, 5, 7} );
	}
	
	/** tags to pick up the buttons from the View */
	private String[] tags = new String[] { "1","2","3","4","5","6","7","8","9" };
	/** references to the {@link FlippoView}s on the screen. */
	private FlippoView[] board = new FlippoView[9];
	/** sensor manager to handle accelerometer events. */
	private SensorManager sensorManager;
	/** the previous time we detected a sensor event. */
	private long lastUpdate = -1;
	
	private float x, y, z;
	private float xold, yold, zold;
	private static int SHAKE_TRESHOLD = 800;
	
	private static final int DIALOG_GAME_OVER = 1;
	
	private static final int DIALOG_CONFIRM_NEW = 2;
	
	Random random = new Random(System.currentTimeMillis());
	
    /** 
     * Set the main content view and build up the board. 
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate");
        setContentView(R.layout.main);
        View maintable = findViewById(R.id.maintable);
        for (int i=0; i<9; i++) {
        	board[i] = (FlippoView) maintable.findViewWithTag(tags[i]);
        }
        newGame();
    }
    
    /**
     * Handle a FlippoView click. The item clicked was already toggled, figure out which
     * additional buttons to toggle from the {@link #rules}.
     * @param view the {@link FlippoView} which was clicked.
     */
    public void clickHandler(View view) {
    	if (view instanceof RadioButton) {
    		String tag = (String) view.getTag();
    		Log.d(TAG,"flipped button "+tag);
    		for (int ix : rules.get(tag)) {
    			board[ix].toggle();
    		}
       		if (checkWinning()) {
       			showDialog(DIALOG_GAME_OVER);
       		}
    	}
    }
    
    /**
     * Handler for the "new game" button.
     * @param view the current view.
     */
    public void startNew(View view) {
    	newGame();
    }
    
    /**
     * Randomize the game, check if we did not accidentally create a win.
     */
    public void newGame() {
    	for (int i=0; i<9; i++) {
    		board[i].setChecked(random.nextBoolean());
    	}
    	if (checkWinning()) {
    		board[0].toggle();
    	}
    }
    
    /**
     * TODO: Open a new view with explanations.
     * @param view
     */
    public void showHelp(View view) {
    	Intent helpIntent = new Intent(this, HelpActivity.class);
    	startActivity(helpIntent);
//    	displayToast("todo: show meaningful help ;-)");
    }
    
    /**
     * Check the view for a winning board. A board is winning if all the buttons are unchecked
     * except for the center one.
     * @return <code>true</code> for a winning board.
     */
    private boolean checkWinning() {
    	boolean result =
    	         !board[0].isChecked() && !board[1].isChecked() && !board[2].isChecked() &&
    	         !board[3].isChecked() &&  board[4].isChecked() && !board[5].isChecked() &&
    	         !board[6].isChecked() && !board[7].isChecked() && !board[8].isChecked();	
    	return result;
    }
    
    /**
     * Handles the dialogs for the game:
     * {@link FlippoActivity#DIALOG_GAME_OVER}: the game is solved: present a dialog with the
     * options of starting a new game, or quitting.
     */
    @Override
    protected Dialog onCreateDialog(int id) {
    	switch (id) {
    	case DIALOG_GAME_OVER:
    		return new AlertDialog.Builder(this)
    			.setIcon(R.drawable.icon)
    			.setTitle(R.string.game_over_title)
    			.setMessage(R.string.game_over_text)
    			.setPositiveButton(R.string.button_newgame, new DialogInterface.OnClickListener() {
					
					public void onClick(DialogInterface arg0, int arg1) {
						newGame();						
					}
				})
				.setNegativeButton(R.string.button_quit, new DialogInterface.OnClickListener() {					
					public void onClick(DialogInterface dialog, int which) {
						if (sensorManager != null) {
							sensorManager.unregisterListener(FlippoActivity.this);
							sensorManager = null;
						}
						finish();
					}
				})
				.create();
    	case DIALOG_CONFIRM_NEW:
    		return new AlertDialog.Builder(this)
			.setIcon(R.drawable.icon)
			.setTitle(R.string.newgame_title)
			.setMessage(R.string.newgame_text)
			.setPositiveButton(R.string.button_yes, new DialogInterface.OnClickListener() {
				
				public void onClick(DialogInterface arg0, int arg1) {
					newGame();						
				}
			})
			.setNegativeButton(R.string.button_no, new DialogInterface.OnClickListener() {					
				public void onClick(DialogInterface dialog, int which) {
					// do nothing, game remains as is
				}
			})
			.create();
    	}
    	return null;
    }

	public void onAccuracyChanged(Sensor sensor, int accuracy) {
		// ignored
	}

	public void onSensorChanged(SensorEvent event) {
//		displayToast("Sensor event: "+event);
		if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
			long now = System.currentTimeMillis();
			long delta = now - lastUpdate;
			if (delta > 100) {
				lastUpdate = now;
				x = event.values[0];
				y = event.values[1];
				z = event.values[2];
				float speed = (x + y + z - xold - yold - zold) / delta * 10000;
				xold = x;
				yold = y;
				zold = z;
				if (speed > SHAKE_TRESHOLD) {
					showDialog(DIALOG_CONFIRM_NEW);
				}
			}
		}
		
	}
	
	/**
	 * Another Activity has moved in front of us. Stop listening for sensor events and destroy our
	 * SensorManager reference.
	 */
	@Override
	protected void onPause() {
		super.onPause();
		Log.d(TAG, " onPause");
		if (sensorManager != null) {
			sensorManager.unregisterListener(this);
			sensorManager = null;
		}
	}
	
	/**
	 * The activity is just starting or has been resumed. (Re)register for accellerometer events.
	 */
	@Override
	protected void onResume() {
		super.onResume();
		Log.d(TAG," onResume");
		registerForAccellerometer();
	}

	/**
	 * Register this Avtivity for accelerometer events.
	 */
	private void registerForAccellerometer() {
		Log.d(TAG, "registerForAccellerometer");
		sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
		Sensor accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		boolean sensorSupported = sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_GAME);
		if (!sensorSupported) {
			Log.d(TAG, "no accellerometer support, unregister");
			sensorManager.unregisterListener(this);
		}
	}

    /**
     * Quick message popup.
     * @param msg
     */
    private void displayToast(String msg) {
    	Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
    }
    
}